using Fantasy.SourceGenerator.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Fantasy.SourceGenerator.Generators
{
    [Generator]
    public partial class ProtoBufGenerator : IIncrementalGenerator
    {
        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            // 查找所有实现了 EventSystem 相关抽象类的类
            var protoBufTypes = context.SyntaxProvider
                .CreateSyntaxProvider(
                    predicate: static (node, _) => IsProtoBufClass(node),
                    transform: static (ctx, _) => GetProtoBufInfo(ctx))
                .Where(static info => info != null)
                .Collect();
            // 组合编译信息和找到的类型
            var compilationAndTypes = context.CompilationProvider.Combine(protoBufTypes);
            // 注册源代码输出
            context.RegisterSourceOutput(compilationAndTypes, static (spc, source) =>
            {
                // 检查1: 是否定义了 FANTASY_NET 或 FANTASY_UNITY 预编译符号
                if (!CompilationHelper.HasFantasyDefine(source.Left))
                {
                    return;
                }

                // 检查2: 是否引用了 Fantasy 框架的核心类型
                if (source.Left.GetTypeByMetadataName("Fantasy.Assembly.IProtoBufRegistrar") == null)
                {
                    return;
                }

                GenerateRegistrationCode(spc, source.Left, source.Right!);
            });
        }

        /// <summary>
        /// 生成注册代码
        /// </summary>
        private static void GenerateRegistrationCode(
            SourceProductionContext context,
            Compilation compilation,
            IEnumerable<ProtoBufTypeInfo> protoBufTypeInfos)
        {
            var protoBufList = protoBufTypeInfos.ToList();
            // 获取当前程序集名称（仅用于注释）
            var assemblyName = compilation.AssemblyName ?? "Unknown";
            var builder = new SourceCodeBuilder();
            // 添加文件头
            builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
            // 添加 using
            builder.AddUsings(
                "System",
                "System.Collections.Generic",
                "Fantasy.Assembly",
                "Fantasy.DataStructure.Collection",
                "Fantasy.Event"
            );
            builder.AppendLine();
            // 开始命名空间（固定使用 Fantasy.Generated）
            builder.BeginNamespace("Fantasy.Generated");
            // 开始类定义（实现 IEventSystemRegistrar 接口）
            builder.AddXmlComment($"Auto-generated ProtoBuf registration class for {assemblyName}");
            builder.BeginClass("ProtoBufRegistrar", "internal sealed", "IProtoBufRegistrar");
            // 生成 ForEach 方法
            GetProtoBufTypesMethod(builder, protoBufList);
            builder.AppendLine();
            // 结束类和命名空间
            builder.EndClass();
            builder.EndNamespace();
            // 输出源代码
            context.AddSource("ProtoBufRegistrar.g.cs", builder.ToString());
        }

        private static void GetProtoBufTypesMethod(SourceCodeBuilder builder, List<ProtoBufTypeInfo> protoBufList)
        {
            builder.AddXmlComment("Register all Prorobuf to the runtimeTypeModel");
            builder.BeginMethod("public List<Type> GetProtoBufTypes()");
            
            if (protoBufList.Any())
            {
                builder.AppendLine($"return new List<Type>({protoBufList.Count})");
                builder.AppendLine("{");
                builder.Indent();
                foreach (var system in protoBufList)
                {
                    builder.AppendLine($"typeof({system.ProtoBufFullName}),");
                }
                builder.Unindent();
                builder.AppendLine("};");
                builder.AppendLine();
            }
            else
            {
                builder.AppendLine($"return new List<Type>();");
            }
            
            builder.EndMethod();
        }

        private static bool IsProtoBufClass(SyntaxNode node)
        {
            if (node is not ClassDeclarationSyntax classDecl)
            {
                return false;
            }
            
            // 必须有基类型列表（实现接口）
            if (classDecl.BaseList == null || classDecl.BaseList.Types.Count == 0)
            {
                return false;
            }

            return classDecl.BaseList.Types.Any(baseType => baseType.Type.ToString() == "IProto");
        }

        private static ProtoBufTypeInfo? GetProtoBufInfo(GeneratorSyntaxContext context)
        {
            var classDecl = (ClassDeclarationSyntax)context.Node;
            var symbol = context.SemanticModel.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;

            if (symbol == null || !symbol.IsInstantiable())
            {
                return null;
            }
            
            return new ProtoBufTypeInfo(
                symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
                symbol.Name
            );
        }
        
        /// <summary>
        /// ProtoBuf 类型信息
        /// </summary>
        private sealed record ProtoBufTypeInfo(
            string ProtoBufFullName,
            string ProtoBufName
        );
    }
}